using System;
using System.Collections.Generic;
using System.Text;
using Logic.Exceptions;
using Logic.IB;
using Data.Probabilities;

namespace Logic.Sequential
{
    class SeqData : Logic.Sequential.Isequential
    {
        private ProbabilityData probData;
       // private DistanceFunction df;
        private Cell[] theGroups;
        private int numberOfGroups;
        private double[] minimalLose;
        private double sumOfMinmalLose;
        private int newGroupNumber;
        private bool sameGroup = false;
        Cell removedCell;

        #region constructors
        public SeqData(int numberOfGroups, ProbabilityData probData)
        {
            this.probData = probData;
            this.theGroups = new Cell[probData.NumberOfGenomes];
            this.removedCell = new Cell(0, 1);
            NumberOfGroups = numberOfGroups;
            this.minimalLose = new double[numberOfGroups];
        //    this.df = new DistanceFunction(probData);
            this.newGroupNumber = 0;
            this.sumOfMinmalLose = double.MaxValue-1;
        }

        #endregion

        #region geters and seters
        public double SumOfMinimalLose
        {
            get
            {
                return this.sumOfMinmalLose;
            }
        }

        public int NumberOfGroups
        {
            get
            {
                return this.numberOfGroups;
            }
            set
            {
                try
                {
                    if (value < 2)
                        throw new NumberOutOfRangeException();
                    else
                        this.numberOfGroups = value;
                }
                catch (NumberOutOfRangeException e)
                {
                    Console.WriteLine("{0}", e.Message);
                }   
            }
        }

        public Cell[] TheGroups
        {
            get
            {
                return this.theGroups;
            }
        }
        /*Maybe i wont need this 1*/
        public double[] getMinimalLose()
        {
            return this.minimalLose;
        }
        #endregion

        #region Methoods
        /*
            Make a random partition.
            Each genome gets a number of group between [1, numberOfGroups]
         */
        public int makePartition()
        {
            Random randomObject = new Random();
            int genomeNumber;
            for (genomeNumber = 0; genomeNumber < this.theGroups.Length; genomeNumber++)
                this.theGroups[genomeNumber] = new Cell(genomeNumber, randomObject.Next(1, NumberOfGroups + 1));
            return genomeNumber;
        }
        /*
            Create a Cell that will hold the removed genome data
         */
        public void removedGenome(int genomeNumber)
        {
            this.removedCell.GenomeNumber = genomeNumber;
            this.removedCell.GroupNumber = this.theGroups[genomeNumber].GroupNumber;
        }

        /*
            compute the Lost from joining the removed genome to each one of the groups.
            returns the number of the group that this genome should be added into.
         */
        public void computeMinimum()
        {
         //  double removedGenomeMarginalDistribution = this.probData.getGenomeMarginalDistribution(this.removedCell.GenomeNumber);
         //   double [] removedGenomeConditionalDistribution = this.probData.getRaw(this.removedCell.GenomeNumber);
            /*Init the minimal lost array*/
            for(int i=0; i<this.numberOfGroups;i++)
                this.minimalLose[i] = 0;
            foreach (Cell c in theGroups)
            {
                if (this.removedCell.GenomeNumber == c.GenomeNumber)
                    continue;
             //   this.minimalLose[c.GroupNumber - 1] = this.minimalLose[c.GroupNumber - 1] + this.df.dist(removedCell.GenomeNumber, c.GenomeNumber);
                this.minimalLose[c.GroupNumber - 1] = this.minimalLose[c.GroupNumber - 1] + DistanceFunction.getDistance(removedCell.GenomeNumber, c.GenomeNumber);
            }
            /*Find the minimum lose and return its group number*/
            double minimalLostValue = Double.MaxValue;
            double tempSum = 0;
            for (int i = 0; i < this.NumberOfGroups; i++)
            {
                tempSum = tempSum + minimalLose[i];
                if (minimalLose[i] < minimalLostValue)
                {
                    minimalLostValue = minimalLose[i];
                    this.newGroupNumber = i + 1;
                }
            }
            if (tempSum < this.sumOfMinmalLose)
                this.sumOfMinmalLose = tempSum;
        }

        public void merge()
        {
            if (this.newGroupNumber == this.removedCell.GroupNumber)
                this.sameGroup = true;
            else
            {
                this.theGroups[this.removedCell.GenomeNumber].GroupNumber = this.newGroupNumber;
                this.sameGroup = false;
            }
        }

        public double calculateSumOfMinimalLost()
        {
            double sum=0;
            foreach (double lost in this.minimalLose)
                sum = sum + lost;
            return sum;
        }

        public bool isSameGroup()
        {
            return this.sameGroup;
        }
        #endregion
    }
}